#include <windows.h>
#include <commctrl.h>

#include "support.h"
#include "window.h"
#include "config.h"

#pragma data_seg(".SHARDATA")
static HWND sh_receiver = 0;
static HHOOK sh_cbt_hook = 0;
static HHOOK sh_shell_hook = 0;
static HHOOK sh_callwndproc_hook = 0;
int bypass_hook = 0;
#pragma data_seg()

void bypass_hooks(int on)
{
	bypass_hook = on;
}

static void send_track(HWND wnd)
{
	SendMessage(sh_receiver, TrackMsg, (WPARAM)wnd, 0);
}

static void send_untrack(HWND wnd)
{
	SendMessage(sh_receiver, UntrackMsg, (WPARAM)wnd, 0);
}

static void send_track_all(void)
{
	SendMessage(sh_receiver, TrackAllMsg, 0, 0);
}

static LRESULT send_activate(HWND wnd)
{
	return SendMessage(sh_receiver, ActivateMsg, (WPARAM)wnd, 0);
}

static void send_minimize(HWND wnd)
{
	SendMessage(sh_receiver, MinimizeMsg, (WPARAM)wnd, 0);
}

LRESULT CALLBACK cbt_fn(int code, WPARAM wp, LPARAM lp)
{
	HWND wnd = (HWND)wp;
	
	#if !Cfg_HotTracking
	if(code == HCBT_MOVESIZE) 
		send_track(wnd);
	#endif
	
	if(code == HCBT_DESTROYWND)
		send_untrack(wnd);
	else if(code == HCBT_ACTIVATE)
	{
		if(bypass_hook)
			return 0;
		
		if(send_activate(wnd))
			return 0;
		
		bypass_hook = 1;
			SetActiveWindow(wnd);
		bypass_hook = 0;
		
		send_track(wnd);
		return 1;
	}
	else if(code == HCBT_MINMAX)
	{
		if(LOWORD(lp) == SW_MINIMIZE)
			send_minimize(wnd);
	}
		
	return 0;
}

LRESULT CALLBACK shell_fn(int code, WPARAM wp, LPARAM lp)
{
	if(code == HSHELL_GETMINRECT)
	{
		RECT *out = (RECT*)lp;
		
		out->left   = SendMessage(sh_receiver, GetMinRectMsg, wp, 0);
		out->top    = SendMessage(sh_receiver, GetMinRectMsg, wp, 1);
		out->right  = SendMessage(sh_receiver, GetMinRectMsg, wp, 2);
		out->bottom = SendMessage(sh_receiver, GetMinRectMsg, wp, 3);
	}
	else if(code == HSHELL_WINDOWACTIVATED)
	{
		if(wp)
			send_track((HWND)wp);
		else
			send_activate(0);
	}
	
	return 0;
}

LRESULT CALLBACK callwndproc_fn(int code, WPARAM wp, LPARAM lp)
{
	CWPRETSTRUCT *cwp = (CWPRETSTRUCT*)lp;
	
	if(cwp->message == WM_SETTEXT)
		send_track(cwp->hwnd);
	
	#if Cfg_HotTracking
	if(cwp->message == WM_MOVE)
		send_track(cwp->hwnd);
	#endif
	
	return CallNextHookEx(0, code, wp, lp);
}

void install_hooks(HWND msgs_to)
{
	HINSTANCE module = GetModuleHandle("MonMan.dll");
	
	sh_receiver = msgs_to;
	sh_cbt_hook = SetWindowsHookEx(
		WH_CBT, 
		&cbt_fn,
		module,
		0
	);
	
	sh_shell_hook = SetWindowsHookEx(
		WH_SHELL,
		&shell_fn,
		module,
		0
	);
	
	sh_callwndproc_hook = SetWindowsHookEx(
		WH_CALLWNDPROCRET,
		&callwndproc_fn,
		module,
		0
	);
	
	if(!sh_cbt_hook) fatal("CBT Hook failed");
	if(!sh_shell_hook) fatal("Shell Hook failed");
	if(!sh_callwndproc_hook) fatal("CallWndProc Hook failed");
}

void free_hooks(void)
{
	UnhookWindowsHookEx(sh_callwndproc_hook);
	UnhookWindowsHookEx(sh_shell_hook);
	UnhookWindowsHookEx(sh_cbt_hook);
}

